using Exploder.Core;
using Exploder.Core.Math;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;

namespace Exploder
{
	public class ExploderObject : MonoBehaviour
	{
		[Serializable]
		public class SFXOption
		{
			public AudioClip ExplosionSoundClip;

			public AudioClip FragmentSoundClip;

			public GameObject FragmentEmitter;

			public float HitSoundTimeout;

			public int EmitersMax;

			public float ParticleTimeout;

			public SFXOption Clone()
			{
				SFXOption sFXOption = new SFXOption();
				sFXOption.ExplosionSoundClip = ExplosionSoundClip;
				sFXOption.FragmentSoundClip = FragmentSoundClip;
				sFXOption.FragmentEmitter = FragmentEmitter;
				sFXOption.HitSoundTimeout = HitSoundTimeout;
				sFXOption.EmitersMax = EmitersMax;
				sFXOption.ParticleTimeout = ParticleTimeout;
				return sFXOption;
			}
		}

		[Serializable]
		public class FragmentOption
		{
			public bool FreezePositionX;

			public bool FreezePositionY;

			public bool FreezePositionZ;

			public bool FreezeRotationX;

			public bool FreezeRotationY;

			public bool FreezeRotationZ;

			public string Layer;

			public float MaxVelocity;

			public bool InheritParentPhysicsProperty;

			public float Mass;

			public bool UseGravity;

			public bool DisableColliders;

			public float AngularVelocity;

			public float MaxAngularVelocity;

			public Vector3 AngularVelocityVector;

			public bool RandomAngularVelocityVector;

			public Material FragmentMaterial;

			public FragmentOption Clone()
			{
				FragmentOption fragmentOption = new FragmentOption();
				fragmentOption.FreezePositionX = FreezePositionX;
				fragmentOption.FreezePositionY = FreezePositionY;
				fragmentOption.FreezePositionZ = FreezePositionZ;
				fragmentOption.FreezeRotationX = FreezeRotationX;
				fragmentOption.FreezeRotationY = FreezeRotationY;
				fragmentOption.FreezeRotationZ = FreezeRotationZ;
				fragmentOption.Layer = Layer;
				fragmentOption.Mass = Mass;
				fragmentOption.DisableColliders = DisableColliders;
				fragmentOption.UseGravity = UseGravity;
				fragmentOption.MaxVelocity = MaxVelocity;
				fragmentOption.MaxAngularVelocity = MaxAngularVelocity;
				fragmentOption.InheritParentPhysicsProperty = InheritParentPhysicsProperty;
				fragmentOption.AngularVelocity = AngularVelocity;
				fragmentOption.AngularVelocityVector = AngularVelocityVector;
				fragmentOption.RandomAngularVelocityVector = RandomAngularVelocityVector;
				fragmentOption.FragmentMaterial = FragmentMaterial;
				return fragmentOption;
			}
		}

		public enum ExplosionState
		{
			ExplosionStarted,
			ExplosionFinished
		}

		private enum State
		{
			None,
			Preprocess,
			ProcessCutter,
			IsolateMeshIslands,
			PostprocessInit,
			Postprocess,
			DryRun
		}

		private struct CutMesh
		{
			public Mesh mesh;

			public Material material;

			public Transform transform;

			public Transform parent;

			public Vector3 position;

			public Quaternion rotation;

			public Vector3 localScale;

			public GameObject original;

			public Vector3 centroid;

			public float distance;

			public int vertices;

			public int level;

			public int fragments;

			public ExploderOption option;

			public GameObject skinnedOriginal;
		}

		private struct MeshData
		{
			public Mesh sharedMesh;

			public Material sharedMaterial;

			public GameObject gameObject;

			public GameObject parentObject;

			public GameObject skinnedBakeOriginal;

			public Vector3 centroid;
		}

		public delegate void OnExplosion(float timeMS, ExplosionState state);

		public delegate void OnCracked();

		public static string Tag = "Exploder";

		public bool DontUseTag;

		public float Radius = 10f;

		public Vector3 CubeRadius = Vector3.zero;

		public bool UseCubeRadius;

		public Vector3 ForceVector = Vector3.up;

		public bool UseForceVector;

		public float Force = 30f;

		public float FrameBudget = 15f;

		public int TargetFragments = 30;

		public DeactivateOptions DeactivateOptions;

		public float DeactivateTimeout = 10f;

		public FadeoutOptions FadeoutOptions;

		public bool MeshColliders;

		public bool ExplodeSelf = true;

		public bool DisableRadiusScan;

		public bool HideSelf = true;

		public bool DestroyOriginalObject;

		public bool ExplodeFragments = true;

		public bool UniformFragmentDistribution;

		public bool SplitMeshIslands;

		public bool AllowOpenMeshCutting;

		public int FragmentPoolSize = 200;

		public GameObject FragmentPrefab;

		public bool Use2DCollision;

		public SFXOption SFXOptions = new SFXOption
		{
			ExplosionSoundClip = null,
			FragmentSoundClip = null,
			FragmentEmitter = null,
			HitSoundTimeout = 0.3f,
			EmitersMax = 1000,
			ParticleTimeout = -1f
		};

		public FragmentOption FragmentOptions = new FragmentOption
		{
			FreezePositionX = false,
			FreezePositionY = false,
			FreezePositionZ = false,
			FreezeRotationX = false,
			FreezeRotationY = false,
			FreezeRotationZ = false,
			Layer = "Default",
			Mass = 20f,
			MaxVelocity = 1000f,
			DisableColliders = false,
			UseGravity = true,
			InheritParentPhysicsProperty = true,
			AngularVelocity = 1f,
			AngularVelocityVector = Vector3.up,
			MaxAngularVelocity = 7f,
			RandomAngularVelocityVector = true,
			FragmentMaterial = null
		};

		internal GameObject Target;

		private OnCracked CrackedCallback;

		private bool crack;

		private bool cracked;

		private ExploderSettings settings;

		private State state;

		private ExploderQueue queue;

		private MeshCutter cutter;

		private Stopwatch timer;

		private HashSet<CutMesh> newFragments;

		private HashSet<CutMesh> meshToRemove;

		private HashSet<CutMesh> meshSet;

		private int[] levelCount;

		private int poolIdx;

		private List<CutMesh> postList;

		private List<Fragment> pool;

		private Vector3 crackedPos;

		private Quaternion crackedRot;

		private bool splitMeshIslands;

		private List<CutMesh> islands;

		private AudioSource audioSource;

		public void ExplodeRadius()
		{
			ExplodeRadius(null);
		}

		public void ExplodeRadius(OnExplosion callback)
		{
			queue.Enqueue(callback, null);
		}

		public void ExplodeObject(GameObject obj)
		{
			ExplodeObject(obj, null);
		}

		public void ExplodeObject(GameObject obj, OnExplosion callback)
		{
			queue.Enqueue(callback, obj);
		}

		public void StartExplosionFromQueue(ExploderSettings settings)
		{
			this.settings = settings;
			state = State.Preprocess;
		}

		public void CrackRadius()
		{
			CrackRadius(null);
		}

		public void CrackRadius(OnCracked callback)
		{
			if (!crack)
			{
				CrackedCallback = callback;
				crack = true;
				cracked = false;
				ExplodeRadius(null);
			}
		}

		public void CrackObject(GameObject obj)
		{
			CrackObject(obj, null);
		}

		public void CrackObject(GameObject obj, OnCracked callback)
		{
			if (!crack)
			{
				CrackedCallback = callback;
				crack = true;
				cracked = false;
				ExplodeObject(obj, null);
			}
		}

		public void ExplodeCracked(OnExplosion callback)
		{
			if (cracked)
			{
				PostCrackExplode(callback);
				crack = false;
			}
		}

		public void ExplodeCracked()
		{
			ExplodeCracked(null);
		}

		private void Awake()
		{
			cutter = new MeshCutter();
			cutter.Init(512, 512);
			UnityEngine.Random.seed = DateTime.Now.Millisecond;
			settings = new ExploderSettings(this);
			bool use2DCollision = settings.Use2DCollision;
			FragmentPool.Instance.Allocate(settings.FragmentPoolSize, settings.MeshColliders, use2DCollision, settings.FragmentPrefab);
			FragmentPool.Instance.SetDeactivateOptions(settings.DeactivateOptions, settings.FadeoutOptions, settings.DeactivateTimeout);
			FragmentPool.Instance.SetExplodableFragments(settings.ExplodeFragments, settings.DontUseTag);
			FragmentPool.Instance.SetFragmentPhysicsOptions(settings.FragmentOptions, use2DCollision);
			FragmentPool.Instance.SetSFXOptions(settings.SFXOptions);
			timer = new Stopwatch();
			queue = new ExploderQueue(this);
			if (settings.ExplodeSelf)
			{
				if (settings.DontUseTag)
				{
					base.gameObject.AddComponent<Explodable>();
				}
				else
				{
					base.gameObject.tag = "Exploder";
				}
			}
			state = State.DryRun;
			PreAllocateBuffers();
			state = State.None;
			if ((bool)settings.SFXOptions.ExplosionSoundClip)
			{
				audioSource = base.gameObject.GetComponent<AudioSource>();
				if (!audioSource)
				{
					audioSource = base.gameObject.AddComponent<AudioSource>();
				}
			}
		}

		private void PreAllocateBuffers()
		{
			newFragments = new HashSet<CutMesh>();
			meshToRemove = new HashSet<CutMesh>();
			meshSet = new HashSet<CutMesh>();
			for (int i = 0; i < 64; i++)
			{
				meshSet.Add(default(CutMesh));
			}
			levelCount = new int[64];
			Preprocess();
			ProcessCutter(out long _);
		}

		private void OnDrawGizmos()
		{
			if (base.enabled && (!ExplodeSelf || !DisableRadiusScan))
			{
				Gizmos.color = Color.red;
				if (UseCubeRadius)
				{
					Vector3 centroid = ExploderUtils.GetCentroid(base.gameObject);
					Gizmos.matrix = base.transform.localToWorldMatrix;
					Gizmos.DrawWireCube(base.transform.InverseTransformPoint(centroid), CubeRadius);
				}
				else
				{
					Gizmos.DrawWireSphere(ExploderUtils.GetCentroid(base.gameObject), Radius);
				}
			}
		}

		private bool IsInRadius(GameObject o)
		{
			Vector3 centroid = ExploderUtils.GetCentroid(o);
			if (settings.UseCubeRadius)
			{
				Vector3 vector = base.transform.InverseTransformPoint(centroid);
				Vector3 vector2 = base.transform.InverseTransformPoint(settings.Position);
				return Mathf.Abs(vector.x - vector2.x) < settings.CubeRadius.x && Mathf.Abs(vector.y - vector2.y) < settings.CubeRadius.y && Mathf.Abs(vector.z - vector2.z) < settings.CubeRadius.z;
			}
			return settings.Radius * settings.Radius > (centroid - settings.Position).sqrMagnitude;
		}

		private int GetLevelFragments(int level, int fragmentsMax)
		{
			return fragmentsMax * 2 / (level * level + level) + 1;
		}

		private int GetLevel(float distance, float radius)
		{
			float num = distance / radius * 6f;
			int value = (int)num / 2 + 1;
			return Mathf.Clamp(value, 0, 10);
		}

		private List<MeshData> GetMeshData(GameObject obj)
		{
			MeshRenderer[] componentsInChildren = obj.GetComponentsInChildren<MeshRenderer>();
			MeshFilter[] componentsInChildren2 = obj.GetComponentsInChildren<MeshFilter>();
			if (componentsInChildren.Length != componentsInChildren2.Length)
			{
				return new List<MeshData>();
			}
			List<MeshData> list = new List<MeshData>(componentsInChildren.Length);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				if (!(componentsInChildren2[i].sharedMesh == null))
				{
					if (!componentsInChildren2[i].sharedMesh || !componentsInChildren2[i].sharedMesh.isReadable)
					{
						UnityEngine.Debug.LogWarning("Mesh is not readable: " + componentsInChildren2[i].name);
					}
					else
					{
						list.Add(new MeshData
						{
							sharedMesh = componentsInChildren2[i].sharedMesh,
							sharedMaterial = componentsInChildren[i].sharedMaterial,
							gameObject = componentsInChildren[i].gameObject,
							centroid = componentsInChildren[i].bounds.center,
							parentObject = obj
						});
					}
				}
			}
			SkinnedMeshRenderer[] componentsInChildren3 = obj.GetComponentsInChildren<SkinnedMeshRenderer>();
			for (int j = 0; j < componentsInChildren3.Length; j++)
			{
				Mesh mesh = new Mesh();
				componentsInChildren3[j].BakeMesh(mesh);
				GameObject gameObject = new GameObject("BakeSkin");
				MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
				meshFilter.sharedMesh = mesh;
				MeshRenderer meshRenderer = gameObject.AddComponent<MeshRenderer>();
				meshRenderer.sharedMaterial = componentsInChildren3[j].material;
				gameObject.transform.position = obj.transform.position;
				gameObject.transform.rotation = obj.transform.rotation;
				ExploderUtils.SetVisible(gameObject, status: false);
				list.Add(new MeshData
				{
					sharedMesh = mesh,
					sharedMaterial = meshRenderer.sharedMaterial,
					gameObject = gameObject,
					centroid = meshRenderer.bounds.center,
					parentObject = gameObject,
					skinnedBakeOriginal = obj
				});
			}
			return list;
		}

		private bool IsExplodable(GameObject obj)
		{
			if (settings.DontUseTag)
			{
				return obj.GetComponent<Explodable>() != null;
			}
			return obj.CompareTag(Tag);
		}

		private List<CutMesh> GetMeshList()
		{
			GameObject[] array = null;
			if ((bool)settings.Target)
			{
				array = new GameObject[1]
				{
					settings.Target
				};
			}
			else if (settings.DontUseTag)
			{
				UnityEngine.Object[] array2 = UnityEngine.Object.FindObjectsOfType(typeof(Explodable));
				List<GameObject> list = new List<GameObject>(array2.Length);
				UnityEngine.Object[] array3 = array2;
				foreach (UnityEngine.Object @object in array3)
				{
					Explodable explodable = (Explodable)@object;
					if ((bool)explodable)
					{
						list.Add(explodable.gameObject);
					}
				}
				array = list.ToArray();
			}
			else
			{
				array = GameObject.FindGameObjectsWithTag("Exploder");
			}
			List<CutMesh> list2 = new List<CutMesh>(array.Length);
			GameObject[] array4 = array;
			foreach (GameObject gameObject in array4)
			{
				if ((!settings.ExplodeSelf && gameObject == base.gameObject) || (gameObject != base.gameObject && settings.ExplodeSelf && settings.DisableRadiusScan) || (!settings.Target && !IsInRadius(gameObject)))
				{
					continue;
				}
				List<MeshData> meshData = GetMeshData(gameObject);
				int count = meshData.Count;
				for (int k = 0; k < count; k++)
				{
					MeshData meshData2 = meshData[k];
					Vector3 centroid = meshData2.centroid;
					if ((bool)settings.Target)
					{
						settings.Position = centroid;
					}
					float magnitude = (centroid - settings.Position).magnitude;
					List<CutMesh> list3 = list2;
					CutMesh item = default(CutMesh);
					MeshData meshData3 = meshData[k];
					item.mesh = meshData3.sharedMesh;
					MeshData meshData4 = meshData[k];
					item.material = meshData4.sharedMaterial;
					MeshData meshData5 = meshData[k];
					item.centroid = meshData5.gameObject.transform.InverseTransformPoint(centroid);
					MeshData meshData6 = meshData[k];
					item.vertices = meshData6.sharedMesh.vertexCount;
					MeshData meshData7 = meshData[k];
					item.transform = meshData7.gameObject.transform;
					MeshData meshData8 = meshData[k];
					item.parent = meshData8.gameObject.transform.parent;
					MeshData meshData9 = meshData[k];
					item.position = meshData9.gameObject.transform.position;
					MeshData meshData10 = meshData[k];
					item.rotation = meshData10.gameObject.transform.rotation;
					MeshData meshData11 = meshData[k];
					item.localScale = meshData11.gameObject.transform.localScale;
					item.distance = magnitude;
					item.level = GetLevel(magnitude, settings.Radius);
					MeshData meshData12 = meshData[k];
					item.original = meshData12.parentObject;
					MeshData meshData13 = meshData[k];
					item.skinnedOriginal = meshData13.skinnedBakeOriginal;
					item.option = gameObject.GetComponent<ExploderOption>();
					list3.Add(item);
				}
			}
			if (list2.Count == 0)
			{
				return list2;
			}
			list2.Sort((CutMesh m0, CutMesh m1) => m0.level.CompareTo(m1.level));
			if (list2.Count > settings.TargetFragments)
			{
				list2.RemoveRange(settings.TargetFragments - 1, list2.Count - settings.TargetFragments);
			}
			CutMesh cutMesh = list2[list2.Count - 1];
			int level = cutMesh.level;
			int levelFragments = GetLevelFragments(level, settings.TargetFragments);
			int num = 0;
			int count2 = list2.Count;
			int[] array5 = new int[level + 1];
			foreach (CutMesh item2 in list2)
			{
				CutMesh current = item2;
				array5[current.level]++;
			}
			for (int l = 0; l < count2; l++)
			{
				CutMesh value = list2[l];
				int num2 = level + 1 - value.level;
				num += (value.fragments = num2 * levelFragments / array5[value.level]);
				list2[l] = value;
				if (num >= settings.TargetFragments)
				{
					value.fragments -= num - settings.TargetFragments;
					num -= num - settings.TargetFragments;
					list2[l] = value;
					break;
				}
			}
			return list2;
		}

		private void Update()
		{
			long cuttingTime = 0L;
			switch (state)
			{
			default:
				return;
			case State.None:
				return;
			case State.Preprocess:
				timer.Reset();
				timer.Start();
				if (!Preprocess())
				{
					OnExplosionFinished(success: false);
					return;
				}
				state = State.ProcessCutter;
				goto case State.ProcessCutter;
			case State.ProcessCutter:
				if (ProcessCutter(out cuttingTime))
				{
					poolIdx = 0;
					postList = new List<CutMesh>(meshSet);
					if (splitMeshIslands)
					{
						islands = new List<CutMesh>(meshSet.Count);
						state = State.IsolateMeshIslands;
						goto case State.IsolateMeshIslands;
					}
					state = State.PostprocessInit;
					goto case State.PostprocessInit;
				}
				return;
			case State.IsolateMeshIslands:
				if (IsolateMeshIslands(ref cuttingTime))
				{
					state = State.PostprocessInit;
					goto case State.PostprocessInit;
				}
				return;
			case State.PostprocessInit:
				InitPostprocess();
				state = State.Postprocess;
				break;
			case State.Postprocess:
				break;
			}
			if (Postprocess(cuttingTime))
			{
				timer.Stop();
			}
		}

		private bool Preprocess()
		{
			List<CutMesh> meshList = GetMeshList();
			if (meshList.Count == 0)
			{
				return false;
			}
			newFragments.Clear();
			meshToRemove.Clear();
			meshSet = new HashSet<CutMesh>(meshList);
			splitMeshIslands = settings.SplitMeshIslands;
			CutMesh cutMesh = meshList[meshList.Count - 1];
			int level = cutMesh.level;
			levelCount = new int[level + 1];
			foreach (CutMesh item in meshSet)
			{
				CutMesh current = item;
				levelCount[current.level] += current.fragments;
			}
			if (settings.UniformFragmentDistribution)
			{
				int[] array = new int[64];
				foreach (CutMesh item2 in meshSet)
				{
					CutMesh current2 = item2;
					array[current2.level]++;
				}
				int num = settings.TargetFragments / meshSet.Count;
				foreach (CutMesh item3 in meshSet)
				{
					CutMesh current3 = item3;
					levelCount[current3.level] = num * array[current3.level];
				}
			}
			return true;
		}

		private bool ProcessCutter(out long cuttingTime)
		{
			Stopwatch stopwatch = new Stopwatch();
			stopwatch.Start();
			bool flag = true;
			bool flag2 = false;
			int num = 0;
			while (flag)
			{
				num++;
				if (num > settings.TargetFragments)
				{
					break;
				}
				int count = meshSet.Count;
				newFragments.Clear();
				meshToRemove.Clear();
				flag = false;
				foreach (CutMesh item in meshSet)
				{
					CutMesh current = item;
					if (levelCount[current.level] > 0)
					{
						Vector3 insideUnitSphere = UnityEngine.Random.insideUnitSphere;
						if ((bool)current.transform)
						{
							Exploder.Core.Math.Plane plane = new Exploder.Core.Math.Plane(insideUnitSphere, current.transform.TransformPoint(current.centroid));
							bool triangulateHoles = true;
							Color crossSectionVertexColor = Color.white;
							Vector4 crossUV = new Vector4(0f, 0f, 1f, 1f);
							if ((bool)current.option)
							{
								triangulateHoles = !current.option.Plane2D;
								crossSectionVertexColor = current.option.CrossSectionVertexColor;
								crossUV = current.option.CrossSectionUV;
								splitMeshIslands |= current.option.SplitMeshIslands;
							}
							if (settings.Use2DCollision)
							{
								triangulateHoles = false;
							}
							List<CutterMesh> meshes = null;
							cutter.Cut(current.mesh, current.transform, plane, triangulateHoles, settings.AllowOpenMeshCutting, ref meshes, crossSectionVertexColor, crossUV);
							flag = true;
							if (meshes != null)
							{
								foreach (CutterMesh item2 in meshes)
								{
									CutterMesh current2 = item2;
									newFragments.Add(new CutMesh
									{
										mesh = current2.mesh,
										centroid = current2.centroid,
										material = current.material,
										vertices = current.vertices,
										transform = current.transform,
										distance = current.distance,
										level = current.level,
										fragments = current.fragments,
										original = current.original,
										skinnedOriginal = current.skinnedOriginal,
										parent = current.transform.parent,
										position = current.transform.position,
										rotation = current.transform.rotation,
										localScale = current.transform.localScale,
										option = current.option
									});
								}
								meshToRemove.Add(current);
								levelCount[current.level]--;
								if (count + newFragments.Count - meshToRemove.Count >= settings.TargetFragments)
								{
									cuttingTime = stopwatch.ElapsedMilliseconds;
									meshSet.ExceptWith(meshToRemove);
									meshSet.UnionWith(newFragments);
									return true;
								}
								if ((float)stopwatch.ElapsedMilliseconds > settings.FrameBudget)
								{
									flag2 = true;
									break;
								}
							}
						}
					}
				}
				meshSet.ExceptWith(meshToRemove);
				meshSet.UnionWith(newFragments);
				if (flag2)
				{
					break;
				}
			}
			cuttingTime = stopwatch.ElapsedMilliseconds;
			if (!flag2)
			{
				return true;
			}
			return false;
		}

		private bool IsolateMeshIslands(ref long timeOffset)
		{
			Stopwatch stopwatch = new Stopwatch();
			stopwatch.Start();
			int count = postList.Count;
			while (poolIdx < count)
			{
				CutMesh item = postList[poolIdx];
				poolIdx++;
				bool flag = false;
				if (settings.SplitMeshIslands || ((bool)item.option && item.option.SplitMeshIslands))
				{
					List<CutterMesh> list = MeshUtils.IsolateMeshIslands(item.mesh);
					if (list != null)
					{
						flag = true;
						foreach (CutterMesh item2 in list)
						{
							CutterMesh current = item2;
							islands.Add(new CutMesh
							{
								mesh = current.mesh,
								centroid = current.centroid,
								material = item.material,
								vertices = item.vertices,
								transform = item.transform,
								distance = item.distance,
								level = item.level,
								fragments = item.fragments,
								original = item.original,
								skinnedOriginal = item.skinnedOriginal,
								parent = item.transform.parent,
								position = item.transform.position,
								rotation = item.transform.rotation,
								localScale = item.transform.localScale,
								option = item.option
							});
						}
					}
				}
				if (!flag)
				{
					islands.Add(item);
				}
				if ((float)(stopwatch.ElapsedMilliseconds + timeOffset) > settings.FrameBudget)
				{
					return false;
				}
			}
			postList = islands;
			return true;
		}

		private void InitPostprocess()
		{
			int count = postList.Count;
			bool use2DCollision = settings.Use2DCollision;
			FragmentPool.Instance.Allocate(count, settings.MeshColliders, use2DCollision, settings.FragmentPrefab);
			FragmentPool.Instance.SetDeactivateOptions(settings.DeactivateOptions, settings.FadeoutOptions, settings.DeactivateTimeout);
			FragmentPool.Instance.SetExplodableFragments(settings.ExplodeFragments, settings.DontUseTag);
			FragmentPool.Instance.SetFragmentPhysicsOptions(settings.FragmentOptions, use2DCollision);
			FragmentPool.Instance.SetSFXOptions(settings.SFXOptions);
			poolIdx = 0;
			pool = FragmentPool.Instance.GetAvailableFragments(count);
			if (settings.Callback != null)
			{
				settings.Callback(timer.ElapsedMilliseconds, ExplosionState.ExplosionStarted);
			}
			if ((bool)settings.SFXOptions.ExplosionSoundClip)
			{
				if (!audioSource)
				{
					audioSource = base.gameObject.AddComponent<AudioSource>();
				}
				audioSource.PlayOneShot(settings.SFXOptions.ExplosionSoundClip);
			}
			if (!crack)
			{
				return;
			}
			FragmentPool.Instance.ResetTransform();
			if (postList.Count > 0)
			{
				CutMesh cutMesh = postList[0];
				if ((bool)cutMesh.skinnedOriginal)
				{
					CutMesh cutMesh2 = postList[0];
					crackedPos = cutMesh2.skinnedOriginal.transform.position;
					CutMesh cutMesh3 = postList[0];
					crackedRot = cutMesh3.skinnedOriginal.transform.rotation;
				}
				else
				{
					CutMesh cutMesh4 = postList[0];
					crackedPos = cutMesh4.original.transform.position;
					CutMesh cutMesh5 = postList[0];
					crackedRot = cutMesh5.original.transform.rotation;
				}
			}
		}

		private void PostCrackExplode(OnExplosion callback)
		{
			callback?.Invoke(0f, ExplosionState.ExplosionStarted);
			int count = postList.Count;
			poolIdx = 0;
			Vector3 vector = Vector3.zero;
			Quaternion quaternion = Quaternion.identity;
			if (postList.Count > 0)
			{
				CutMesh cutMesh = postList[0];
				if ((bool)cutMesh.skinnedOriginal)
				{
					CutMesh cutMesh2 = postList[0];
					vector = cutMesh2.skinnedOriginal.transform.position - crackedPos;
					CutMesh cutMesh3 = postList[0];
					quaternion = cutMesh3.skinnedOriginal.transform.rotation * Quaternion.Inverse(crackedRot);
				}
				else
				{
					CutMesh cutMesh4 = postList[0];
					vector = cutMesh4.original.transform.position - crackedPos;
					CutMesh cutMesh5 = postList[0];
					quaternion = cutMesh5.original.transform.rotation * Quaternion.Inverse(crackedRot);
				}
			}
			while (poolIdx < count)
			{
				Fragment fragment = pool[poolIdx];
				CutMesh cutMesh6 = postList[poolIdx];
				poolIdx++;
				if (cutMesh6.original != base.gameObject)
				{
					ExploderUtils.SetActiveRecursively(cutMesh6.original, status: false);
				}
				else
				{
					ExploderUtils.EnableCollider(cutMesh6.original, status: false);
					ExploderUtils.SetVisible(cutMesh6.original, status: false);
				}
				if ((bool)cutMesh6.skinnedOriginal && cutMesh6.skinnedOriginal != base.gameObject)
				{
					ExploderUtils.SetActiveRecursively(cutMesh6.skinnedOriginal, status: false);
				}
				else
				{
					ExploderUtils.EnableCollider(cutMesh6.skinnedOriginal, status: false);
					ExploderUtils.SetVisible(cutMesh6.skinnedOriginal, status: false);
				}
				fragment.transform.position += vector;
				fragment.transform.rotation *= quaternion;
				fragment.Explode();
			}
			if (settings.DestroyOriginalObject)
			{
				foreach (CutMesh post in postList)
				{
					CutMesh current = post;
					if ((bool)current.original && !current.original.GetComponent<Fragment>())
					{
						UnityEngine.Object.Destroy(current.original);
					}
					if ((bool)current.skinnedOriginal)
					{
						UnityEngine.Object.Destroy(current.skinnedOriginal);
					}
				}
			}
			if (settings.ExplodeSelf && !settings.DestroyOriginalObject)
			{
				ExploderUtils.SetActiveRecursively(base.gameObject, status: false);
			}
			if (settings.HideSelf)
			{
				ExploderUtils.SetActiveRecursively(base.gameObject, status: false);
			}
			settings.Callback = callback;
			OnExplosionFinished(success: true);
		}

		private bool Postprocess(long timeOffset)
		{
			Stopwatch stopwatch = new Stopwatch();
			stopwatch.Start();
			int count = postList.Count;
			while (poolIdx < count)
			{
				Fragment fragment = pool[poolIdx];
				CutMesh cutMesh = postList[poolIdx];
				poolIdx++;
				if (!cutMesh.original)
				{
					continue;
				}
				if (crack)
				{
					ExploderUtils.SetActiveRecursively(fragment.gameObject, status: false);
				}
				fragment.meshFilter.sharedMesh = cutMesh.mesh;
				if ((bool)cutMesh.option && (bool)cutMesh.option.FragmentMaterial)
				{
					fragment.meshRenderer.sharedMaterial = cutMesh.option.FragmentMaterial;
				}
				else if (settings.FragmentOptions.FragmentMaterial != null)
				{
					fragment.meshRenderer.sharedMaterial = settings.FragmentOptions.FragmentMaterial;
				}
				else
				{
					fragment.meshRenderer.sharedMaterial = cutMesh.material;
				}
				cutMesh.mesh.RecalculateBounds();
				Transform parent = fragment.transform.parent;
				fragment.transform.parent = cutMesh.parent;
				fragment.transform.position = cutMesh.position;
				fragment.transform.rotation = cutMesh.rotation;
				fragment.transform.localScale = cutMesh.localScale;
				fragment.transform.parent = null;
				fragment.transform.parent = parent;
				if (!crack)
				{
					if (cutMesh.original != base.gameObject)
					{
						ExploderUtils.SetActiveRecursively(cutMesh.original, status: false);
					}
					else
					{
						ExploderUtils.EnableCollider(cutMesh.original, status: false);
						ExploderUtils.SetVisible(cutMesh.original, status: false);
					}
					if ((bool)cutMesh.skinnedOriginal && cutMesh.skinnedOriginal != base.gameObject)
					{
						ExploderUtils.SetActiveRecursively(cutMesh.skinnedOriginal, status: false);
					}
					else
					{
						ExploderUtils.EnableCollider(cutMesh.skinnedOriginal, status: false);
						ExploderUtils.SetVisible(cutMesh.skinnedOriginal, status: false);
					}
				}
				bool flag = (bool)cutMesh.option && cutMesh.option.Plane2D;
				bool use2DCollision = settings.Use2DCollision;
				if (!settings.FragmentOptions.DisableColliders)
				{
					if (settings.MeshColliders && !use2DCollision)
					{
						if (!flag)
						{
							fragment.meshCollider.sharedMesh = cutMesh.mesh;
						}
					}
					else if (settings.Use2DCollision)
					{
						MeshUtils.GeneratePolygonCollider(fragment.polygonCollider2D, cutMesh.mesh);
					}
					else
					{
						fragment.boxCollider.center = cutMesh.mesh.bounds.center;
						fragment.boxCollider.size = cutMesh.mesh.bounds.extents;
					}
				}
				if ((bool)cutMesh.option)
				{
					cutMesh.option.DuplicateSettings(fragment.options);
				}
				if (!crack)
				{
					fragment.Explode();
				}
				float force = settings.Force;
				if ((bool)cutMesh.option && cutMesh.option.UseLocalForce)
				{
					force = cutMesh.option.Force;
				}
				fragment.ApplyExplosion(cutMesh.transform, cutMesh.centroid, settings.Position, settings.FragmentOptions, settings.UseForceVector, settings.ForceVector, force, cutMesh.original, settings.TargetFragments);
				if (!((float)(stopwatch.ElapsedMilliseconds + timeOffset) > settings.FrameBudget))
				{
					continue;
				}
				return false;
			}
			if (!crack)
			{
				if (settings.DestroyOriginalObject)
				{
					foreach (CutMesh post in postList)
					{
						CutMesh current = post;
						if ((bool)current.original && !current.original.GetComponent<Fragment>())
						{
							UnityEngine.Object.Destroy(current.original);
						}
						if ((bool)current.skinnedOriginal)
						{
							UnityEngine.Object.Destroy(current.skinnedOriginal);
						}
					}
				}
				if (settings.ExplodeSelf && !settings.DestroyOriginalObject)
				{
					ExploderUtils.SetActiveRecursively(base.gameObject, status: false);
				}
				if (settings.HideSelf)
				{
					ExploderUtils.SetActiveRecursively(base.gameObject, status: false);
				}
				OnExplosionFinished(success: true);
			}
			else
			{
				cracked = true;
				if (CrackedCallback != null)
				{
					CrackedCallback();
				}
			}
			return true;
		}

		private void OnExplosionFinished(bool success)
		{
			if (settings.Callback != null)
			{
				if (!success)
				{
					settings.Callback(timer.ElapsedMilliseconds, ExplosionState.ExplosionStarted);
					OnExplosionStarted();
				}
				settings.Callback(timer.ElapsedMilliseconds, ExplosionState.ExplosionFinished);
			}
			state = State.None;
			queue.OnExplosionFinished(settings.id);
		}

		private void OnExplosionStarted()
		{
		}
	}
}
